#ifndef DW_MATHS_VECTOR_H
#	define DW_MATHS_VECTOR_H

#include "maths/matrix.h"

namespace dw {
	namespace maths {

template< typename T, unsigned N >
struct Point;

template< typename T, unsigned N >
struct Vector;

template< typename T, unsigned M, unsigned N >
struct Matrix;

template< typename T, unsigned N >
inline bool operator==(const Vector< T, N >& lhs, const Vector< T, N >& rhs);

template< typename T, unsigned N >
inline bool operator!=(const Vector< T, N >& lhs, const Vector< T, N >& rhs);

template< typename T, unsigned N >
inline Vector< T, N >								operator-(const Vector< T, N >& rhs);

template< typename T, unsigned N >
inline Vector< T, N >								operator+(const Vector< T, N >& lhs, const Vector< T, N >& rhs);

template< typename T, unsigned N >
inline Vector< T, N >								operator-(const Vector< T, N >& lhs, const Vector< T, N >& rhs);

template< typename T, unsigned N >
inline Vector< T, N >								operator*(const Vector< T, N >& lhs, const Vector< T, N >& rhs);

template< typename T, unsigned N >
inline Vector< T, N >								operator*(const Vector< T, N >& lhs, const T& rhs);

template< typename T, unsigned N >
inline Vector< T, N >								operator*(const T& lhs, const Vector< T, N >& rhs);

template< typename T, unsigned N >
inline Vector< T, N >								operator*(const Vector< T, N >& lhs, const Matrix< T, N, N >& rhs);

template< typename T, unsigned N >
inline Vector< T, N >								operator*(const Matrix< T, N, N >& lhs, const Vector< T, N >& rhs);



template< typename T, unsigned N >
struct Vector
{
public : //! @name Constants
	//! @{
	static const Vector< T, N > zero;
	//! @}

public : //! @name Data
	//! @{
	T v[N];
	//! @}

public : //! @name Construction & destruction
	//! @{
	Vector();
	Vector(const Vector< T, N >& ref);
	explicit Vector(const T* src);
	//! @}

public :
	const T& operator[](const unsigned i) const { dwAssert(i < N, "Invalid parameter: i is out of range"); return v[i]; }
	T& operator[](const unsigned i) { dwAssert(i < N, "Invalid parameter: i is out of range"); return v[i]; }
	Vector< T, N >& operator=(const Vector< T, N >& rhs);
	Vector< T, N >& operator+=(const Vector< T, N >& rhs);
	Vector< T, N >& operator-=(const Vector< T, N >& rhs);
	Vector< T, N >& operator*=(const T& rhs);
	Vector< T, N >& operator*=(const Vector< T, N >& rhs);
	Vector< T, N >& operator/=(const T& rhs);

	Vector< T, N >& reset();
	Vector< T, N >& normalize();
	T sqrLength() const ;
	T length() const ;
	std::string toString() const ;

	static T dot(const Vector< T, N >& v0, const Vector< T, N >& v1);
};

template< typename T >
struct Vector< T, 2 >
{
public : //! @name Constants
	//! @{
	static const Vector< T, 2 > zero;
	static const Vector< T, 2 > unitX;
	static const Vector< T, 2 > unitY;
	//! @}

public : //! @name Data
	//! @{
	union
	{
		struct 
		{
			T x, y;
		};
		T v[2];
	};
	//! @}
	
public : //! @name Construction & destruction
	//! @{
	Vector();
	Vector(const T& pX, const T& pY);
	Vector(const Vector< T, 2 >& ref);
	explicit Vector(const T* src);
	//! @}

public :
	const T& operator[](const unsigned i) const { dwAssert(i < 2, "Invalid parameter: i is out of range"); return v[i]; }
	T& operator[](const unsigned i) { dwAssert(i < 2, "Invalid parameter: i is out of range"); return v[i]; }
	Vector< T, 2 >& operator=(const Vector< T, 2 >& rhs);
	Vector< T, 2 >& operator+=(const Vector< T, 2 >& rhs);
	Vector< T, 2 >& operator-=(const Vector< T, 2 >& rhs);
	Vector< T, 2 >& operator*=(const T& rhs);
	Vector< T, 2 >& operator*=(const Vector< T, 2 >& rhs);
	Vector< T, 2 >& operator/=(const T& rhs);

	Vector< T, 2 >& reset();
	Vector< T, 2 >& normalize();
	T sqrLength() const ;
	T length() const ;
	std::string toString() const ;

	static T dot(const Vector< T, 2 >& v0, const Vector< T, 2 >& v1);
};

template< typename T >
struct Vector< T, 3 >
{
public : //! @name Constants
	//! @{
	static const Vector< T, 3 > zero;
	static const Vector< T, 3 > unitX;
	static const Vector< T, 3 > unitY;
	static const Vector< T, 3 > unitZ;
	//! @}

public : //! @name Data
	//! @{
	union
	{
		struct 
		{
			T x, y, z;
		};
		T v[3];
	};
	//! @}
	
public : //! @name Construction & destruction
	//! @{
	Vector();
	Vector(const T& pX, const T& pY, const T& pZ);
	Vector(const Vector< T, 3 >& ref);
	explicit Vector(const T* src);
	explicit Vector(const Vector< T, 4 >& ref);
	//! @}

public :
	const T& operator[](const unsigned i) const { dwAssert(i < 3, "Invalid parameter: i is out of range"); return v[i]; }
	T& operator[](const unsigned i) { dwAssert(i < 3, "Invalid parameter: i is out of range"); return v[i]; }
	Vector< T, 3 >& operator=(const Vector< T, 3 >& rhs);
	Vector< T, 3 >& operator+=(const Vector< T, 3 >& rhs);
	Vector< T, 3 >& operator-=(const Vector< T, 3 >& rhs);
	Vector< T, 3 >& operator*=(const T& rhs);
	Vector< T, 3 >& operator*=(const Vector< T, 3 >& rhs);
	Vector< T, 3 >& operator/=(const T& rhs);

	Vector< T, 3 >& reset();
	Vector< T, 3 >& normalize();
	T sqrLength() const ;
	T length() const ;
	std::string toString() const ;

	bool isNormalized() const ;

	static T dot(const Vector< T, 3 >& v0, const Vector< T, 3 >& v1);
	static Vector< T, 3 > cross(const Vector< T, 3 >& v0, const Vector< T, 3 >& v1);

	static Vector< T, 3 > min(const Vector< T, 3 >& v0, const Vector< T, 3 >& v1);
	static Vector< T, 3 > max(const Vector< T, 3 >& v0, const Vector< T, 3 >& v1);
	static Vector< T, 3 > clamp(const Vector< T, 3 >& v, const Vector< T, 3 >& min, const Vector< T, 3 >& max);
};

template< typename T >
struct Vector< T, 4 >
{
public : //! @name Constants
	//! @{
	static const Vector< T, 4 > zero;
	static const Vector< T, 4 > unitX;
	static const Vector< T, 4 > unitY;
	static const Vector< T, 4 > unitZ;
	static const Vector< T, 4 > unitW;
	//! @}
	
public : //! @name Data
	//! @{
	union
	{
		struct 
		{
			T x, y, z, w;
		};
		T v[4];
	};
	//! @}
	
public : //! @name Construction & destruction
	//! @{
	Vector();
	Vector(const T& pX, const T& pY, const T& pZ, const T& pW);
	Vector(const Vector< T, 4 >& ref);
	explicit Vector(const T* src);
	explicit Vector(const Vector< T, 3 >& ref, const T& pW = 0);
	explicit Vector(const Point< T, 3 >& ref, const T& pW = 1);
	//! @}

public :
	const T& operator[](const unsigned i) const { dwAssert(i < 4, "Invalid parameter: i is out of range"); return v[i]; }
	T& operator[](const unsigned i) { dwAssert(i < 4, "Invalid parameter: i is out of range"); return v[i]; }
	Vector< T, 4 >& operator=(const Vector< T, 4 >& rhs);
	Vector< T, 4 >& operator+=(const Vector< T, 4 >& rhs);
	Vector< T, 4 >& operator-=(const Vector< T, 4 >& rhs);
	Vector< T, 4 >& operator*=(const T& rhs);
	Vector< T, 4 >& operator*=(const Vector< T, 4 >& rhs);
	Vector< T, 4 >& operator/=(const T& rhs);

	Vector< T, 4 >& set(const T& pX, const T& pY, const T& pZ, const T& pW);
	Vector< T, 4 >& reset();
	Vector< T, 4 >& normalize();
	T sqrLength() const ;
	T length() const ;
	std::string toString() const ;

	bool isNormalized() const ;

	static T dot(const Vector< T, 4 >& v0, const Vector< T, 4 >& v1);
};

typedef Vector< dw::int32, 2 > Vector2i;
typedef Vector< dw::uint32, 2 > Vector2ui;
typedef Vector< dw::float32, 2 > Vector2f;
typedef Vector< dw::float64, 2 > Vector2d;

typedef Vector< dw::int32, 3 > Vector3i;
typedef Vector< dw::uint32, 3 > Vector3ui;
typedef Vector< dw::float32, 3 > Vector3f;
typedef Vector< dw::float64, 3 > Vector3d;

typedef Vector< dw::int32, 4 > Vector4i;
typedef Vector< dw::uint32, 4 > Vector4ui;
typedef Vector< dw::float32, 4 > Vector4f;
typedef Vector< dw::float64, 4 > Vector4d;

typedef Vector2i ivec2;
typedef Vector2ui uvec2;
typedef Vector2f vec2;
typedef Vector2d dvec2;

typedef Vector3i ivec3;
typedef Vector3ui uvec3;
typedef Vector3f vec3;
typedef Vector3d dvec3;

typedef Vector4i ivec4;
typedef Vector4ui uvec4;
typedef Vector4f vec4;
typedef Vector4d dvec4;

	} // namespace maths
} // namespace dw 

#include "maths/vector.inl"

#endif // !DW_MATHS_VECTOR_H
